package biz.datalk.industrialland.admin.modules.online.config.hook.impl;

import biz.datalk.commons.helper.collection.ListHelper;
import biz.datalk.commons.utils.IdProvider;
import biz.datalk.commons.utils.json.JsonUtil;
import biz.datalk.industrialland.admin.modules.common.servie.ICommonService;
import biz.datalk.industrialland.admin.modules.online.config.cfg.enums.SqlOperationTypeEnum;
import biz.datalk.industrialland.admin.modules.online.config.hook.HookSplicingResult;
import biz.datalk.industrialland.admin.modules.online.config.hook.HookValidParamsResult;
import biz.datalk.industrialland.admin.modules.online.config.hook.IDynfromHook;
import biz.datalk.industrialland.admin.modules.online.config.util.DynformFieldUtil;
import biz.datalk.industrialland.admin.modules.online.dynform.pojo.dto.DynformAutoDTO;
import biz.datalk.industrialland.admin.modules.online.dynform.pojo.dto.DynformFieldDTO;
import biz.datalk.industrialland.admin.modules.online.dynform.pojo.dto.DynformHeadDTO;
import biz.datalk.industrialland.admin.modules.online.dynform.pojo.po.DynformField;
import biz.datalk.industrialland.admin.modules.online.dynform.pojo.po.DynformHead;
import biz.datalk.industrialland.common.def.enums.DBYNEnum;
import biz.datalk.industrialland.common.exception.ServiceException;
import biz.datalk.industrialland.common.result.UnifyResult;
import biz.datalk.industrialland.common.util.FieldUtil;
import biz.datalk.industrialland.common.util.SpringContextUtils;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.*;

/**
 * @author tarofang@163.com
 * @date 2019-12-30
 */
public class DefaultDynfromHookImpl implements IDynfromHook {
    @Override
    public void beforeList(DynformAutoDTO dynformAutoDTO) {

    }

    @Override
    public UnifyResult afterList(DynformAutoDTO dynformAutoDTO, UnifyResult result) {
        return result;
    }

    @Override
    public void beforeDetail(DynformAutoDTO dynformAutoDTO) {

    }

    @Override
    public UnifyResult afterDetail(DynformAutoDTO dynformAutoDTO, UnifyResult result) {
        return result;
    }

    @Override
    public void beforeInsert(DynformAutoDTO dynformAutoDTO) {

    }

    @Override
    public UnifyResult afterInsert(DynformAutoDTO dynformAutoDTO, UnifyResult result) {
        return result;
    }

    @Override
    public void beforeUpdate(DynformAutoDTO dynformAutoDTO) {

    }

    @Override
    public UnifyResult afterUpdate(DynformAutoDTO dynformAutoDTO, UnifyResult result) {
        return result;
    }

    @Override
    public void beforeDelete(DynformAutoDTO dynformAutoDTO) {

    }

    @Override
    public UnifyResult afterDelete(DynformAutoDTO dynformAutoDTO, UnifyResult result) {
        return result;
    }

    @Override
    public HookSplicingResult splicingListRowSql(DynformHead tableInfo, Map<String, String> queryMap, DynformAutoDTO dynformAutoDTO, List<DynformFieldDTO> fields) {
        List<DynformFieldDTO> pageShowListFields = DynformFieldUtil.getPageShowListFields(fields);
        if (CollectionUtils.isEmpty(pageShowListFields)) {
            throw new ServiceException("未设置列表显示字段");
        }

        // 获取查询字段
        ListHelper listHelper = ListHelper.create();
        List<String> fieldNames = listHelper.getValList(pageShowListFields, "getFieldName", DynformFieldDTO.class, String.class);
        String queryFields = listHelper.combine(fieldNames, ",");

        // 查询条件
        List<DynformFieldDTO> queryFieldList = DynformFieldUtil.getQueryFields(fields);
        String searchConstr = searchCon(queryMap, queryFieldList);

        String sqlPattern = "SELECT {0} FROM {1} WHERE 1=1 {2}";

        // 排序

        // 分页
        if (StringUtils.equals(tableInfo.getIsPagination(), DBYNEnum.Y.name())) {
            int offset = (dynformAutoDTO.getPageNumber() - 1) * dynformAutoDTO.getPageSize();
            sqlPattern = sqlPattern.concat(" LIMIT ").concat(String.valueOf(offset)).concat(",").concat(String.valueOf(dynformAutoDTO.getPageSize()));
        }

        String sql = MessageFormat.format(sqlPattern, queryFields, tableInfo.getTableName(), searchConstr);

        return new HookSplicingResult(sql, null);
    }

    protected String searchCon(Map<String, String> queryMap, List<DynformFieldDTO> queryFieldList) {
        ListHelper listHelper = ListHelper.create();
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : queryMap.entrySet()) {
            if (StringUtils.isBlank(entry.getValue())) {
                continue;
            }
            String key = entry.getKey();
            key = FieldUtil.hump2Underline(key);
            DynformFieldDTO one = listHelper.findOne(queryFieldList, "getFieldName", key, DynformFieldDTO.class, String.class);
            if (one == null) {
                continue;
            }
            // varchar/int/double/datetime/timestamp/decimal/text/mediumtext/blob
            // String fieldType = one.getFieldType();
            // qeuryResMap.put(key, entry.getValue());
            Object val = null;
            switch (one.getFieldType()) {
                case "varchar":
                case "text":
                case "mediumtext":
                case "datetime":
                case "timestamp":
                    val = entry.getValue();
                    break;
                case "int":
                    val = Integer.valueOf(entry.getValue());
                    break;
                case "double":
                    val = Double.valueOf(entry.getValue());
                    break;
                default:
                    break;
            }
            if (val == null) {
                continue;
            }

            sb.append(" AND ").append(key);
            if (val instanceof String) {
                String str = (String) val;
                if (str.startsWith("*") || str.endsWith("*")) {
                    str = str.startsWith("*") ? "%" + str.substring(1) : str;
                    str = str.endsWith("*") ? str.substring(0, str.length() - 1) + "%" : str;
                    sb.append(" LIKE ").append("'").append(str).append("'");
                } else {
                    sb.append("=").append("'").append(val).append("'");
                }
            } else {
                sb.append("=").append(val);
            }
        }
        return sb.toString();
    }

    @Override
    public HookSplicingResult splicingListTotalSql(DynformHead tableInfo, Map<String, String> queryMap, DynformAutoDTO dynformAutoDTO, List<DynformFieldDTO> fields) {

        // 查询条件
        List<DynformFieldDTO> queryFieldList = DynformFieldUtil.getQueryFields(fields);
        String searchConstr = searchCon(queryMap, queryFieldList);

        String sqlPattern = "SELECT COUNT(*) FROM {0} WHERE 1=1 {1}";

        String sql = MessageFormat.format(sqlPattern, tableInfo.getTableName(), searchConstr);
        return new HookSplicingResult(sql, null);
    }

    @Override
    public HookSplicingResult splicingDetailSql(DynformHead tableInfo, DynformAutoDTO dynformAutoDTO, List<DynformFieldDTO> fields) {
        List<DynformFieldDTO> pageShowFields = DynformFieldUtil.getPageShowFields(fields, Boolean.TRUE);
        ListHelper listHelper = ListHelper.create();
        List<String> fieldNames = listHelper.getValList(pageShowFields, "getFieldName", DynformFieldDTO.class, String.class);
        String fieldStr = listHelper.combine(fieldNames, ",");

        String sqlPattern = "SELECT {0} FROM {1} WHERE id=?";

        String sql = MessageFormat.format(sqlPattern, fieldStr, tableInfo.getTableName());

        List<Object> args = new ArrayList<>();
        args.add(dynformAutoDTO.getId());

        return new HookSplicingResult(sql, args);
    }

    @Override
    public HookSplicingResult splicingInsertSql(DynformHead tableInfo, DynformAutoDTO dynformAutoDTO, List<DynformFieldDTO> fields, HookValidParamsResult validParamsResult) {
        List<Object> args = new ArrayList<>();
        boolean isFirst = true;
        StringBuilder sqlsb = new StringBuilder();
        LinkedHashMap<String, Object> fvPkMap = validParamsResult.getFvPkMap();
        for (Map.Entry<String, Object> entry : fvPkMap.entrySet()) {
            if (!isFirst) {
                sqlsb.append(",");
            } else {
                isFirst = false;
            }
            sqlsb.append(entry.getKey());
            args.add(IdProvider.createUUIDId());
        }
        LinkedHashMap<String, Object> fvMap = validParamsResult.getFvMap();
        for (Map.Entry<String, Object> entry : fvMap.entrySet()) {
            if (!isFirst) {
                sqlsb.append(",");
            } else {
                isFirst = false;
            }
            sqlsb.append(entry.getKey());
            args.add(entry.getValue());
        }


        String tableName = StringUtils.trimToEmpty(tableInfo.getTableName());
        String fieldStr = sqlsb.toString();

        sqlsb.delete(0, sqlsb.length());
        for (int index = 0, size = args.size(); index < size; index++) {
            if (index > 0) {
                sqlsb.append(",");
            }
            sqlsb.append("?");
        }
        String temporarySymbol = sqlsb.toString();

        String sqlPattern = "INSERT INTO {0} ({1}) values ({2})";
        String sql = MessageFormat.format(sqlPattern, tableName, fieldStr, temporarySymbol);

        return new HookSplicingResult(sql, args);
    }

    @Override
    public HookSplicingResult splicingUpdateSql(DynformHead tableInfo, DynformAutoDTO dynformAutoDTO, List<DynformFieldDTO> fields, HookValidParamsResult validParamsResult) {
        // UPDATE %s SET f1=v,f2=x WHERE pk1=x1 AND pk2=x2
        List<Object> args = new ArrayList<>();
        boolean isFirst = true;
        StringBuilder sqlsb = new StringBuilder();
        LinkedHashMap<String, Object> fvMap = validParamsResult.getFvMap();
        for (Map.Entry<String, Object> entry : fvMap.entrySet()) {
            if (!isFirst) {
                sqlsb.append(",");
            } else {
                isFirst = false;
            }
            sqlsb.append(entry.getKey()).append("=?");
            args.add(entry.getValue());
        }
        String fieldStr = sqlsb.toString();

        sqlsb.delete(0, sqlsb.length());
        isFirst = true;
        LinkedHashMap<String, Object> fvPkMap = validParamsResult.getFvPkMap();
        for (Map.Entry<String, Object> entry : fvPkMap.entrySet()) {
            if (!isFirst) {
                sqlsb.append(" AND ");
            } else {
                isFirst = false;
            }
            sqlsb.append(entry.getKey()).append("=?");
            args.add(entry.getValue());
        }
        String whereStr = sqlsb.toString();


        String tableName = StringUtils.trimToEmpty(tableInfo.getTableName());

        String sqlPattern = "UPDATE {0} SET {1} WHERE {2}";
        String sql = MessageFormat.format(sqlPattern, tableName, fieldStr, whereStr);

        return new HookSplicingResult(sql, args);
    }

    @Override
    public HookSplicingResult splicingDeleteSql(DynformHead tableInfo, DynformAutoDTO dynformAutoDTO, List<DynformFieldDTO> fields) {
        if (StringUtils.isBlank(dynformAutoDTO.getId())) {
            throw new ServiceException("提交的数据有误");
        }

        String sqlPattern = "DELETE FROM {0} WHERE id=?";
        String sql = MessageFormat.format(sqlPattern, tableInfo.getTableName());
        return new HookSplicingResult(sql, Collections.singletonList(dynformAutoDTO.getId()));
    }

    @Override
    public Object execSql(String sql, List<Object> args, SqlOperationTypeEnum sqlOperationTypeEnum) {
        ICommonService commonService = SpringContextUtils.getBean(ICommonService.class);
        if (SqlOperationTypeEnum.LIST_TOTAL.equals(sqlOperationTypeEnum)) {
            return CollectionUtils.isNotEmpty(args) ? commonService.getCountForJdbc(sql, args.toArray()) : commonService.getCountForJdbc(sql);
        }

        if (SqlOperationTypeEnum.LIST_ROWS.equals(sqlOperationTypeEnum)) {
            return CollectionUtils.isNotEmpty(args) ? commonService.findForJdbc(sql, args.toArray()): commonService.findForJdbc(sql);
        }

        if (SqlOperationTypeEnum.DETAIL.equals(sqlOperationTypeEnum)) {
            return CollectionUtils.isNotEmpty(args) ? commonService.findOneForJdbc(sql, args.toArray()): commonService.findOneForJdbc(sql);
        }

        return CollectionUtils.isNotEmpty(args) ? commonService.update(sql, args.toArray()): commonService.update(sql);
    }

    @Override
    public Map<String, String> parseQueryParams(DynformAutoDTO dynformAutoDTO, HttpServletRequest request, DynformHead table, List<DynformFieldDTO> allFields, SqlOperationTypeEnum sqlOperationTypeEnum) {
        String queryParams = dynformAutoDTO.getQueryParams();
        // @formatter:off
        Map<String, String> queryMap = JsonUtil.json2JavaBean(queryParams, new TypeToken<Map<String, String>>() {}.getType());
        // @formatter:on
        return queryMap;
    }

    @Override
    public Map<String, String> parseParams(DynformAutoDTO dynformAutoDTO, HttpServletRequest request, DynformHead table, List<DynformFieldDTO> allFields, SqlOperationTypeEnum sqlOperationTypeEnum) {
        String jsonParams = dynformAutoDTO.getParams();
        // @formatter:off
        Map<String, String> paramsMap = JsonUtil.json2JavaBean(jsonParams, new TypeToken<Map<String, String>>() {}.getType());
        // @formatter:on
        return paramsMap;
    }

    @Override
    public HookValidParamsResult validParams(DynformAutoDTO dynformAutoDTO, DynformHead table, List<DynformFieldDTO> existsFields, Map<String, String> paramsMap, SqlOperationTypeEnum sqlOperationTypeEnum) {
        List<DynformFieldDTO> pageShowFields = DynformFieldUtil.getPageShowFields(existsFields, Boolean.TRUE);

        boolean isUpdate = SqlOperationTypeEnum.UPDATE.equals(sqlOperationTypeEnum);

        LinkedHashMap<String, Object> fvMap = new LinkedHashMap<>();
        LinkedHashMap<String, Object> fvPkMap = new LinkedHashMap<>();

        for (DynformField field : pageShowFields) {
            String fieldName = field.getFieldName();
            String fieldNameHump = FieldUtil.underline2Hump(fieldName);
            // 提交的数据中 不包含字段
            if (!paramsMap.containsKey(fieldNameHump)) {
                if (StringUtils.equals(field.getPageMustInput(), DBYNEnum.Y.name())) {
                    throw new ServiceException(String.format("%s 必填", field.getFieldComment()));
                }
                if (StringUtils.equals(field.getFieldIsPk(), DBYNEnum.Y.name())) {
                    fvPkMap.put(fieldName, DynformFieldUtil.convertVal(field, field.getFieldDefault()));
                } else {
                    fvMap.put(fieldName, DynformFieldUtil.convertVal(field, field.getFieldDefault()));
                }
                continue;
            }

            // 提交的数据中 包含该字段
            String val = paramsMap.get(fieldNameHump);
            if (val == null) {
                if (StringUtils.equals(field.getPageMustInput(), DBYNEnum.Y.name())) {
                    throw new ServiceException(String.format("%s 必填", field.getFieldComment()));
                }
                if (isUpdate && StringUtils.equals(field.getFieldIsPk(), DBYNEnum.Y.name())) {
                    throw new ServiceException("数据不存在或已被删除");
                }
                continue;
            }

            if (StringUtils.equals(field.getFieldIsPk(), DBYNEnum.Y.name())) {
                if (StringUtils.isEmpty(val)) {
                    throw new ServiceException("数据不存在或已被删除");
                }
                fvPkMap.put(fieldName, DynformFieldUtil.convertVal(field, val));
            } else {
                fvMap.put(fieldName, DynformFieldUtil.convertVal(field, val));
            }
        }

        return new HookValidParamsResult(fvMap, fvPkMap);
    }

    @Override
    public void beforeDoButton(DynformAutoDTO dynformAutoDTO) {
    }

    @Override
    public UnifyResult doButton(DynformHeadDTO tableDTO, String btnCode, Map<String, String> paramsMap) {
        return UnifyResult.success();
    }

    @Override
    public UnifyResult afterDoButton(DynformAutoDTO dynformAutoDTO, UnifyResult result) {
        return result;
    }

    @Override
    public UnifyResult saveDBSuccess(Integer opResInt, DynformAutoDTO dynformAutoDTO, DynformHead table, List<DynformFieldDTO> existsFields, Map<String, String> paramsMap, SqlOperationTypeEnum sqlOperationTypeEnum) {
        return UnifyResult.success();
    }

    @Override
    public HookValidParamsResult beforeExecSQL(HookValidParamsResult validParamsResult, DynformAutoDTO dynformAutoDTO, DynformHead table, List<DynformFieldDTO> fieldList, Map<String, String> paramsMap, SqlOperationTypeEnum sqlOperationTypeEnum) {
        return validParamsResult;
    }
}
